/*
 * Copyright 2017 the original author or authors.
 */

package com.ivo.util;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.reflect.TypeToken;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.springframework.core.io.ClassPathResource;

/**
 * <h1>JSON相关操作辅助类</h1>
 * <p>
 * content
 * </p>
 * <b>Note:</b>
 *
 * @author nexf
 * @since 2015/10/2 13:46
 */
public class JsonUtil {

  private static final String CONFIG_FILE_PATH = "gson.properties";

  private static boolean serializeNulls = false;

  private static String dateFormat = "yyyy-MM-dd HH:mm:ss";

  private static boolean serializeBooleanAsNum = false;

  private static boolean disableHtmlEscaping = false;

  private static Gson gson = null;

  static {
    synchronized (JsonUtil.class) {
      if (gson == null) {
        Reader reader = null;
        Properties properties = null;
        try {
          reader = new BufferedReader(new InputStreamReader(
              (new ClassPathResource(CONFIG_FILE_PATH).getInputStream()), "UTF-8"));
          properties = new Properties();
          properties.load(reader);
          reader.close();
          reader = null;
        } catch (Exception e) {
          ExceptionUtil.warn(e.getMessage());
        } finally {
          if (reader != null) {
            try {
              reader.close();
            } catch (IOException e) {
              ExceptionUtil.warn(e.getMessage());
            }
          }
        }
        if (properties == null) {
          ExceptionUtil.warn("classpath: 未找到消息包配置 {}，将使用默认配置", CONFIG_FILE_PATH);
        } else {
          String serializeNullsValue = properties.getProperty("serializeNulls");
          if (StringUtil.isNotBlank(serializeNullsValue)) {
            serializeNulls = serializeNullsValue.equalsIgnoreCase(Boolean.TRUE.toString());
          } else {
            ExceptionUtil.warn("Gson配置缺少 {} 配置，将使用默认值 {}", "serializeNulls", serializeNulls);
          }

          String dateFormatValue = properties.getProperty("dateFormat");
          if (StringUtil.isNotBlank(dateFormatValue)) {
            dateFormat = dateFormatValue;
          } else {
            ExceptionUtil.warn("Gson配置缺少 {} 配置，将使用默认值", "dateFormat", dateFormat);
          }

          String serializeBooleanAsNumValue = properties.getProperty("serializeBooleanAsNum");
          if (StringUtil.isNotBlank(serializeBooleanAsNumValue)) {
            serializeBooleanAsNum =
                serializeBooleanAsNumValue.equalsIgnoreCase(Boolean.TRUE.toString());
          } else {
            ExceptionUtil.warn("Gson配置缺少 {} 配置，将使用默认值 {}", "serializeBooleanAsNum",
                serializeBooleanAsNum);
          }

          String disableHtmlEscapingValue = properties.getProperty("disableHtmlEscaping");
          if (StringUtil.isNotBlank(disableHtmlEscapingValue)) {
            disableHtmlEscaping =
                disableHtmlEscapingValue.equalsIgnoreCase(Boolean.TRUE.toString());
          } else {
            ExceptionUtil.warn("Gson配置缺少 {} 配置，将使用默认值 {}", "disableHtmlEscaping",
                disableHtmlEscaping);
          }

        }

        GsonBuilder gsonBuilder = new GsonBuilder();
        if (serializeNulls) {
          gsonBuilder.serializeNulls();
        }
        gsonBuilder.setDateFormat(dateFormat);
        if (serializeBooleanAsNum) {
          BooleanSerializer serializer = new BooleanSerializer();
          gsonBuilder.registerTypeAdapter(Boolean.class, serializer);
          gsonBuilder.registerTypeAdapter(boolean.class, serializer);
        }
        if (disableHtmlEscaping) {
          gsonBuilder.disableHtmlEscaping();
        }
        gson = gsonBuilder.create();
      }
    }
  }

  /**
   * 转成json
   *
   * @param object 对象
   * @return 字符串
   */
  public static String toJson(Object object) {
    String gsonString = null;
    if (gson != null) {
      gsonString = gson.toJson(object);
    }
    return gsonString;
  }

  /**
   * 转成bean
   *
   * @param json json字符串
   * @param clazz 类型
   * @return 对象
   */
  public static <T> T fromJson(String json, Class<T> clazz) {
    T t = null;
    if (gson != null) {
      t = gson.fromJson(json, clazz);
    }
    return t;
  }

  /**
   * 转成list中有map的
   *
   * @param json json字符串
   * @return 集合
   */
  public static <T> List<Map<String, T>> toListMaps(String json) {
    List<Map<String, T>> list = null;
    if (gson != null) {
      list = gson.fromJson(json, new TypeToken<List<Map<String, T>>>() {
      }.getType());
    }
    return list;
  }

  /**
   * 转成map的
   *
   * @param json json字符串
   * @return map
   */
  public static <T> Map<String, T> toMaps(String json) {
    Map<String, T> map = null;
    if (gson != null) {
      map = gson.fromJson(json, new TypeToken<Map<String, T>>() {
      }.getType());
    }
    return map;
  }

  /**
   * 转成list 解决泛型问题
   *
   * @param json json字符串
   * @param clazz 类型
   * @return 集合
   */
  public <T> List<T> toList(String json, Class<T> clazz) {
    Gson gson = new Gson();
    List<T> list = new ArrayList<T>();
    JsonArray array = new JsonParser().parse(json).getAsJsonArray();
    for (final JsonElement elem : array) {
      list.add(gson.fromJson(elem, clazz));
    }
    return list;
  }


  public static class BooleanSerializer
      implements JsonSerializer<Boolean>, JsonDeserializer<Boolean> {

    @Override
    public JsonElement serialize(Boolean arg0, Type arg1, JsonSerializationContext arg2) {
      return new JsonPrimitive(arg0 ? 1 : 0);
    }

    @Override
    public Boolean deserialize(JsonElement arg0, Type arg1, JsonDeserializationContext arg2)
        throws JsonParseException {
      return (arg0.getAsInt() == 1 || Boolean.TRUE.toString().equals(arg0.getAsString()));
    }
  }

}
